package org.adorsys.aderp.jpa.impl;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;

import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldFilter;

/**
 * Helps discover the business key field. Actually we define three rules 
 * in this order:
 * 1- The annotation @BusinessKey is Present
 * 2- The columnName contains ignore case "businessKey"
 * 3- The fieldName is: keyKlassSimpleName
 * 
 * @author francis
 *
 */
public class FieldResolver {
	
	public static Field extractBusinessKeyField(Class<?> klass){
		AderpFieldCallback aderpFieldCallback = new AderpFieldCallback();
		ReflectionUtils.doWithFields(klass, aderpFieldCallback, new BusinessKeyAnnotationFilter());

		// Find the field with annotation @BusinessKey
		if (!aderpFieldCallback.getFields().isEmpty()) {
			return aderpFieldCallback.getFields().iterator().next();
		}

		// Find the field with column name carrying businessKey
		ReflectionUtils.doWithFields(klass, aderpFieldCallback, new BkColumnAnnotationFilter());
		if (!aderpFieldCallback.getFields().isEmpty()) {
			return aderpFieldCallback.getFields().iterator().next();
		}
		return null;
	}
	
	 
	public static List<Field> extractImmutableField(Class<?> klass){
		return extractField(klass, new ImmutableAnnotationFilter(), new ImtbleColumnAnnotationFilter());
	}
	
	public static Field extractIdField(Class<?> klass){
		List<Field> fields = extractField(klass, new IdAnnotationFilter());
		if(fields.size()>1) throw new IllegalStateException("Multiple Id fields found " + fields );
		return fields.iterator().next();
	}

	public static Field extractVersionField(Class<?> klass){
		List<Field> fields = extractField(klass, new VersionAnnotationFilter());
		if(fields.size()>1) throw new IllegalStateException("Multiple Version fields found " + fields );
		return fields.iterator().next();
	}

	public static List<Field> extractField(Class<?> klass, FieldFilter... fieldFilters){
		AderpFieldCallback aderpFieldCallback = new AderpFieldCallback();
		for (FieldFilter fieldFilter : fieldFilters) {
			ReflectionUtils.doWithFields(klass, aderpFieldCallback, fieldFilter);
		}
		return aderpFieldCallback.getFields();
	}
	
	public static Method getterMethod(Field field, Class<?> klass){
		String getter = fieldToGetter(field.getName());
		return ReflectionUtils.findMethod(klass, getter);
	}

	public static Method setterMethod(Field field, Class<?> klass){
		String setter = fieldToSetter(field.getName());
		return ReflectionUtils.findMethod(klass, setter, field.getType());
	}

	private static String fieldToGetter(String name)
	{
	    return "get" + name.substring(0, 1).toUpperCase() + name.substring(1);
	}
	
	private static String fieldToSetter(String name)
	{
	    return "set" + name.substring(0, 1).toUpperCase() + name.substring(1);
	}
}
